package engine.util.texture;

import static org.lwjgl.opengl.GL11.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;

import org.lwjgl.util.glu.GLU;

public class TextureLoader {
	private static final String BMP = "bmp";
	private static final String PNG = "png";
	private static final String JPG = "jpg";
	private static final String JPEG = "jpeg";
	private static final String TIFF = "tiff";
	private static final String TIF = "tif";
	private static final String[] EXTENSIONS = {BMP, PNG, JPG, JPEG, TIFF, TIF};
	
	private static HashMap<String, Integer> imageIDCache = new HashMap<String, Integer>();
	private static HashMap<String, RawData> rawDataCache = new HashMap<String, RawData>();
	
	private TextureLoader() {}
	
	/**
	 * Loads an image. Min and Mag filter are set to GL_LINEAR and the t and s wrap is set to GL_REPEAT. The image is not mipmapped. 
	 * @param file
	 * @return
	 */
	public static int loadTexture(String file) {
		return loadTexture(file, GL_LINEAR, GL_LINEAR, false);
	}
	
	/**
	 * Loads an image. The t and s wrap is set to GL_REPEAT. 
	 * @param file
	 * @param minFilter
	 * @param magFilter
	 * @param mipmapped
	 * @return
	 */
	public static int loadTexture(String file, int minFilter, int magFilter, boolean mipmapped) {
		return loadTexture(file, minFilter, magFilter, GL_REPEAT, GL_REPEAT, mipmapped);
	}
	
	/**
	 * Loads an image. 
	 * @param file
	 * @param minFilter
	 * @param magFilter
	 * @param textureWrapS
	 * @param textureWrapT
	 * @param mipmapped
	 * @return
	 */
	public static int loadTexture(String file, int minFilter, int magFilter, int textureWrapS, int textureWrapT, boolean mipmapped) {
		if(imageIDCache.containsKey(file)) {
			return imageIDCache.get(file);
		}
		
		RawData rawData = loadRawData(file);
		
		int texture = glGenTextures();
		glBindTexture(GL_TEXTURE_2D, texture);
		
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, textureWrapS);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, textureWrapT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
		
		rawData.getTextureBuffer().rewind();
		
		if (mipmapped) {
			GLU.gluBuild2DMipmaps(GL_TEXTURE_2D, rawData.getDstPixelFormat(),
					rawData.getWidth(), rawData.getHeight(), rawData.getSrcPixelFormat(),
					GL_UNSIGNED_BYTE, rawData.getTextureBuffer());
		} else {
			glTexImage2D(GL_TEXTURE_2D, 0, rawData.getDstPixelFormat(), rawData.getWidth(), 
					rawData.getHeight(), 0,
					rawData.getSrcPixelFormat(), GL_UNSIGNED_BYTE, rawData.getTextureBuffer());
		}
		
		glBindTexture(GL_TEXTURE_2D, 0);
		
		imageIDCache.put(file, texture);
		
		return texture;
	}
	
	public static void dealloc() {
		for(Integer tex: imageIDCache.values()){
			glDeleteTextures(tex);
		}
	}
	
	private static String getExtension(String file) {
		String ext = null;
        int i = file.lastIndexOf('.');

        if (i > 0 &&  i < file.length() - 1) {
            ext = file.substring(i+1).toLowerCase();
        }
        
        for(i = 0; i < EXTENSIONS.length; i++) {
        	if(ext.equals(EXTENSIONS[i])) {
        		return ext;
        	}
        }
        
        // If no matching extension was found, exit with error
        throw new IllegalArgumentException("The following image extension is not supported: " + ext);
	}
	
	public static RawData loadRawData(String file) {
		if(rawDataCache.containsKey(file)) {
			return rawDataCache.get(file);
		}
		
		URL url = TextureLoader.class.getClassLoader().getResource(file);
		File f = new File(file);
		
		if(url == null && !f.exists()) {
			System.err.println("Cannot find: " + file);
			return null;
		}
		
		InputStream input = null;
		int fileSize = 0;
		
		if(url != null) {
			fileSize = (int)new File(url.getPath()).length();
			
			input = TextureLoader.class.getClassLoader().getResourceAsStream(file);
			
			if(input == null) {
				System.err.println("Error occured while loading file as a url: " + file);
				return null;
			}
		} else {
			fileSize = (int)f.length();
			try {
				input = new FileInputStream(f);
			} catch (FileNotFoundException e) {
				System.err.println("Error occured while loading file as a file: " + file);
				e.printStackTrace();
				return null;
			}
		}
		
		String extension = null;
		try {
			extension = getExtension(file);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
			System.exit(1);
		}
		
		RawData rawData = null;
		
		if(extension.equals(BMP)) {
			rawData = BMPLoader.getBMP(input, fileSize);
		} else if(extension.equals(PNG)) {
			rawData = PNGLoader.getPNG(input, fileSize);
		} else if(extension.equals(JPG) || extension.equals(JPEG)) {
			
		} else if(extension.equals(TIFF) || extension.equals(TIF)) {
			
		}
		
		try {
			input.close();
		} catch (IOException e) {
			System.out.println("Error occured while closing the input stream");
		}
		
		if(rawData == null) {
			System.err.println("The image is not valid!");
			return null;
		}
		
		rawDataCache.put(file, rawData);
		
		return rawData;
	}
}
